from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, func
from sqlalchemy.orm import Session
from passlib.context import CryptContext
from datetime import datetime, timedelta
from typing import Optional, List
import secrets
from src.utils.token_generator import token_generator

from src.models.user import UserInfo
from src.schemas.user import UserCreate, UserUpdate, UserListQuery
from src.utils.logger import get_logger

logger = get_logger("capcut")

# 密码加密配置
import warnings
# 忽略 bcrypt 版本警告
with warnings.catch_warnings():
    warnings.simplefilter("ignore")
    pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# JWT配置
import config
ACCESS_TOKEN_EXPIRE_MINUTES = getattr(config, 'JWT_ACCESS_TOKEN_EXPIRE_MINUTES', 30 * 24 * 60)  # 30天


class UserService:
    """用户服务类"""
    
    # 类常量
    ACCESS_TOKEN_EXPIRE_MINUTES = ACCESS_TOKEN_EXPIRE_MINUTES
    
    @staticmethod
    def hash_password(password: str) -> str:
        """密码加密"""
        return pwd_context.hash(password)
    
    @staticmethod
    def verify_password(plain_password: str, hashed_password: str) -> bool:
        """密码验证"""
        return pwd_context.verify(plain_password, hashed_password)
    
    @staticmethod
    def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
        """创建访问令牌"""
        return token_generator.create_access_token(data, expires_delta)
    
    @staticmethod
    def verify_token(token: str) -> Optional[dict]:
        """验证令牌"""
        return token_generator.verify_token(token)
    
    @staticmethod
    async def create_user(db: AsyncSession, user_data: UserCreate, creator_id: Optional[int] = None) -> UserInfo:
        """创建用户"""
        try:
            # 检查用户名是否已存在
            existing_user = await UserService.get_user_by_username(db, user_data.username)
            if existing_user:
                raise ValueError("用户名已存在")
            
            # 检查邮箱是否已存在
            if user_data.email:
                existing_email = await UserService.get_user_by_email(db, user_data.email)
                if existing_email:
                    raise ValueError("邮箱已存在")
            
            # 创建新用户
            hashed_password = UserService.hash_password(user_data.password)
            
            db_user = UserInfo(
                username=user_data.username,
                password=hashed_password,
                nick_name=user_data.nick_name or user_data.username,
                phone=user_data.phone,
                email=user_data.email,
                avatar=user_data.avatar,
                sex=user_data.sex,
                remark=user_data.remark,
                create_by=creator_id,
                update_by=creator_id
            )
            
            db.add(db_user)
            await db.commit()
            await db.refresh(db_user)
            
            logger.info(f"用户创建成功: {user_data.username}")
            return db_user
            
        except Exception as e:
            await db.rollback()
            logger.error(f"创建用户失败: {str(e)}")
            raise
    
    @staticmethod
    async def get_user_by_id(db: AsyncSession, user_id: int) -> Optional[UserInfo]:
        """根据ID获取用户"""
        try:
            stmt = select(UserInfo).where(
                and_(
                    UserInfo.id == user_id,
                    UserInfo.del_flag == '0'
                )
            )
            result = await db.execute(stmt)
            return result.scalar_one_or_none()
        except Exception as e:
            logger.error(f"获取用户失败: {str(e)}")
            return None
    
    @staticmethod
    async def get_user_by_username(db: AsyncSession, username: str) -> Optional[UserInfo]:
        """根据用户名获取用户"""
        try:
            stmt = select(UserInfo).where(
                and_(
                    UserInfo.username == username,
                    UserInfo.del_flag == '0'
                )
            )
            result = await db.execute(stmt)
            return result.scalar_one_or_none()
        except Exception as e:
            logger.error(f"获取用户失败: {str(e)}")
            return None
    
    @staticmethod
    async def get_user_by_email(db: AsyncSession, email: str) -> Optional[UserInfo]:
        """根据邮箱获取用户"""
        try:
            stmt = select(UserInfo).where(
                and_(
                    UserInfo.email == email,
                    UserInfo.del_flag == '0'
                )
            )
            result = await db.execute(stmt)
            return result.scalar_one_or_none()
        except Exception as e:
            logger.error(f"获取用户失败: {str(e)}")
            return None
    
    @staticmethod
    async def authenticate_user(db: AsyncSession, username: str, password: str) -> Optional[UserInfo]:
        """用户认证"""
        try:
            user = await UserService.get_user_by_username(db, username)
            if not user:
                return None
            
            if not UserService.verify_password(password, user.password):
                return None
            
            if user.status != 1:
                logger.warning(f"用户已被禁用: {username}")
                return None
            
            return user
        except Exception as e:
            logger.error(f"用户认证失败: {str(e)}")
            return None
    
    @staticmethod
    async def update_user(
        db: AsyncSession, 
        user_id: int, 
        user_data: UserUpdate, 
        updater_id: Optional[int] = None
    ) -> Optional[UserInfo]:
        """更新用户信息"""
        try:
            user = await UserService.get_user_by_id(db, user_id)
            if not user:
                return None
            
            # 更新字段
            update_data = user_data.dict(exclude_unset=True)
            
            # 如果要更新密码，先加密
            if 'password' in update_data and update_data['password']:
                update_data['password'] = UserService.hash_password(update_data['password'])
            
            # 检查邮箱是否重复
            if 'email' in update_data and update_data['email']:
                existing_email = await UserService.get_user_by_email(db, update_data['email'])
                if existing_email and existing_email.id != user_id:
                    raise ValueError("邮箱已存在")
            
            # 更新字段
            for field, value in update_data.items():
                if hasattr(user, field):
                    setattr(user, field, value)
            
            user.update_by = updater_id
            user.update_time = datetime.utcnow()
            
            await db.commit()
            await db.refresh(user)
            
            logger.info(f"用户更新成功: {user.username}")
            return user
            
        except Exception as e:
            await db.rollback()
            logger.error(f"更新用户失败: {str(e)}")
            raise
    
    @staticmethod
    async def delete_user(db: AsyncSession, user_id: int, deleter_id: Optional[int] = None) -> bool:
        """删除用户（软删除）"""
        try:
            user = await UserService.get_user_by_id(db, user_id)
            if not user:
                return False
            
            user.del_flag = '2'
            user.update_by = deleter_id
            user.update_time = datetime.utcnow()
            
            await db.commit()
            
            logger.info(f"用户删除成功: {user.username}")
            return True
            
        except Exception as e:
            await db.rollback()
            logger.error(f"删除用户失败: {str(e)}")
            return False
    
    @staticmethod
    async def get_user_list(db: AsyncSession, query: UserListQuery) -> tuple[List[UserInfo], int]:
        """获取用户列表"""
        try:
            # 构建查询条件
            conditions = [UserInfo.del_flag == '0']
            
            if query.username:
                conditions.append(UserInfo.username.like(f"%{query.username}%"))
            
            if query.nick_name:
                conditions.append(UserInfo.nick_name.like(f"%{query.nick_name}%"))
            
            if query.email:
                conditions.append(UserInfo.email.like(f"%{query.email}%"))
            
            if query.phone:
                conditions.append(UserInfo.phone.like(f"%{query.phone}%"))
            
            if query.status is not None:
                conditions.append(UserInfo.status == query.status)
            
            # 查询总数
            count_stmt = select(func.count(UserInfo.id)).where(and_(*conditions))
            count_result = await db.execute(count_stmt)
            total = count_result.scalar()
            
            # 查询数据
            offset = (query.page - 1) * query.page_size
            stmt = (
                select(UserInfo)
                .where(and_(*conditions))
                .order_by(UserInfo.create_time.desc())
                .offset(offset)
                .limit(query.page_size)
            )
            
            result = await db.execute(stmt)
            users = result.scalars().all()
            
            return list(users), total
            
        except Exception as e:
            logger.error(f"获取用户列表失败: {str(e)}")
            return [], 0
    
    @staticmethod
    async def update_login_info(db: AsyncSession, user_id: int, login_ip: str) -> bool:
        """更新登录信息"""
        try:
            user = await UserService.get_user_by_id(db, user_id)
            if not user:
                return False
            
            user.last_login_ip = login_ip
            user.last_login_time = datetime.utcnow()
            
            await db.commit()
            return True
            
        except Exception as e:
            await db.rollback()
            logger.error(f"更新登录信息失败: {str(e)}")
            return False
    
    @staticmethod
    async def change_password(
        db: AsyncSession, 
        user_id: int, 
        old_password: str, 
        new_password: str
    ) -> bool:
        """修改密码"""
        try:
            user = await UserService.get_user_by_id(db, user_id)
            if not user:
                return False
            
            # 验证旧密码
            if not UserService.verify_password(old_password, user.password):
                raise ValueError("旧密码错误")
            
            # 更新密码
            user.password = UserService.hash_password(new_password)
            user.update_time = datetime.utcnow()
            
            await db.commit()
            
            logger.info(f"用户密码修改成功: {user.username}")
            return True
            
        except Exception as e:
            await db.rollback()
            logger.error(f"修改密码失败: {str(e)}")
            raise 